<template>
  <Mapbox :mapOnLoadCB="mapOnLoadCB"></Mapbox>
</template>

<script>
import {ref} from "vue"
import Mapbox from "@/components/common/Mapbox.component.vue"
import MapboxCommonService from "@/service/map/MapboxCommonService"

export default {
  name: "AddHTMLCluster.component",
  components: {Mapbox},
  setup() {

    let mapStore = ref({});
    let mapOnLoadCB = (map) => {
      mapStore.value = map;
      MapboxCommonService.setCZBP(map, [0, 20], 0.3)

      map.addControl(new mapboxgl.NavigationControl());

      // code for creating an SVG donut chart from feature properties
      const createDonutChart = (props) => {
        let offsets = [];
        let counts = [props.mag1, props.mag2, props.mag3, props.mag4, props.mag5];
        let total = 0;
        for (let i = 0; i < counts.length; i++) {
          offsets.push(total);
          total += counts[i];
        }
        let fontSize = total >= 1000 ? 22 : total >= 100 ? 20 : total >= 10 ? 18 : 16;
        let r = total >= 1000 ? 50 : total >= 100 ? 32 : total >= 10 ? 24 : 18;
        let r0 = Math.round(r * 0.6);
        let w = r * 2;

        let html = '<svg width="' + w + '" height="' + w + '" viewbox="0 0 ' + w + ' ' + w +
            '" text-anchor="middle" style="font: ' + fontSize + 'px sans-serif">';

        for (let i = 0; i < counts.length; i++) {
          html += donutSegment(offsets[i] / total, (offsets[i] + counts[i]) / total, r, r0, colors[i]);
        }
        html += '<circle cx="' + r + '" cy="' + r + '" r="' + r0 +
            '" fill="white" /><text dominant-baseline="central" transform="translate(' +
            r + ', ' + r + ')">' + total.toLocaleString() + '</text></svg>';

        let el = document.createElement('div');
        el.innerHTML = html;
        return el.firstChild;
      }

      const donutSegment = (start, end, r, r0, color) => {
        if (end - start === 1) end -= 0.00001;
        let a0 = 2 * Math.PI * (start - 0.25);
        let a1 = 2 * Math.PI * (end - 0.25);
        let x0 = Math.cos(a0), y0 = Math.sin(a0);
        let x1 = Math.cos(a1), y1 = Math.sin(a1);
        let largeArc = end - start > 0.5 ? 1 : 0;

        return ['<path d="M', r + r0 * x0, r + r0 * y0, 'L', r + r * x0, r + r * y0,
          'A', r, r, 0, largeArc, 1, r + r * x1, r + r * y1,
          'L', r + r0 * x1, r + r0 * y1, 'A',
          r0, r0, 0, largeArc, 0, r + r0 * x0, r + r0 * y0,
          '" fill="' + color + '" />'].join(' ');
      }

      // filters for classifying earthquakes into five categories based on magnitude
      let mag1 = ["<", ["get", "mag"], 2];
      let mag2 = ["all", [">=", ["get", "mag"], 2], ["<", ["get", "mag"], 3]];
      let mag3 = ["all", [">=", ["get", "mag"], 3], ["<", ["get", "mag"], 4]];
      let mag4 = ["all", [">=", ["get", "mag"], 4], ["<", ["get", "mag"], 5]];
      let mag5 = [">=", ["get", "mag"], 5];

      // colors to use for the categories
      let colors = ['#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c'];

      // add a clustered GeoJSON source for a sample set of earthquakes
      map.addSource('earthquakes', {
        "type": "geojson",
        "data": `${window.location.origin}/mapbox-test/libs/mapbox/assets/earthquakes.geojson`,
        "cluster": true,
        "clusterRadius": 80,
        "clusterProperties": { // keep separate counts for each magnitude category in a cluster
          "mag1": ["+", ["case", mag1, 1, 0]],
          "mag2": ["+", ["case", mag2, 1, 0]],
          "mag3": ["+", ["case", mag3, 1, 0]],
          "mag4": ["+", ["case", mag4, 1, 0]],
          "mag5": ["+", ["case", mag5, 1, 0]]
        }
      });
      // circle and symbol layers for rendering individual earthquakes (unclustered points)
      map.addLayer({
        "id": "earthquake_circle",
        "type": "circle",
        "source": "earthquakes",
        "filter": ["!=", "cluster", true],
        "paint": {
          "circle-color": ["case",
            mag1, colors[0],
            mag2, colors[1],
            mag3, colors[2],
            mag4, colors[3], colors[4]],
          "circle-opacity": 0.6,
          "circle-radius": 12
        }
      });
      map.addLayer({
        "id": "earthquake_label",
        "type": "symbol",
        "source": "earthquakes",
        "filter": ["!=", "cluster", true],
        "layout": {
          "text-field": ["number-format", ["get", "mag"], {
            "min-fraction-digits": 1,
            "max-fraction-digits": 1
          }],
          "text-font": ["Open Sans Semibold", "Arial Unicode MS Bold"],
          "text-size": 10
        },
        "paint": {
          "text-color": ["case", ["<", ["get", "mag"], 3], "black", "white"]
        }
      });

      // objects for caching and keeping track of HTML marker objects (for performance)
      let markers = {};
      let markersOnScreen = {};

      const updateMarkers = () => {
        let newMarkers = {};
        let features = map.querySourceFeatures('earthquakes');

        // for every cluster on the screen, create an HTML marker for it (if we didn't yet),
        // and add it to the map if it's not there already
        for (let i = 0; i < features.length; i++) {
          let coords = features[i].geometry.coordinates;
          let props = features[i].properties;
          if (!props.cluster) continue;
          let id = props.cluster_id;

          let marker = markers[id];
          if (!marker) {
            let el = createDonutChart(props);
            marker = markers[id] = new mapboxgl.Marker({element: el}).setLngLat(coords);
          }
          newMarkers[id] = marker;

          if (!markersOnScreen[id])
            marker.addTo(map);
        }
        // for every marker we've added previously, remove those that are no longer visible
        for (let id in markersOnScreen) {
          if (!newMarkers[id])
            markersOnScreen[id].remove();
        }
        markersOnScreen = newMarkers;
      }

      // after the GeoJSON data is loaded, update markers on the screen and do so on every map move/moveend
      map.on('data', (e) => {
        if (e.sourceId !== 'earthquakes' || !e.isSourceLoaded) return;

        map.on('move', updateMarkers);
        map.on('moveend', updateMarkers);
        updateMarkers();
      });
    };

    return {
      mapOnLoadCB
    }
  }
}
</script>

<style scoped>

</style>